function fn1() {

    const obj = {
        name: "法外狂徒战三",
        age: 18
    };

    // 默认情况下，它们不可枚举、不可配置、不可写
    Object.defineProperty(obj, "key", {
        // obj.key的默认值
        value: "静态的",

        // 默认不可配置
        // configurable: false,

        // 默认不可写
        writable: true,

        // 默认不可枚举
        enumerable: true
    });

    obj.key = "动态的"
    console.log(obj.key);

    /* 枚举（enum）obj的所有【可枚举属性】 */
    for (let k in obj) {
        console.log(k, obj[k]);
    }


    /* 2. 使用一个包含所有属性的临时对象字面量来明确其属性 */
    // Object.defineProperty(obj, "key2", {
    //     // obj.key2不可被for-in循环枚举
    //     enumerable: false,

    //     // obj.key2不支持自定义设置
    //     configurable: false,

    //     // obj.key2不可写
    //     writable: false,

    //     // 默认值
    //     value: "static",
    // });


}

function fn2() {
    const o = {}; // 创建一个新对象

    // 通过 defineProperty 使用访问器属性描述符添加对象属性的示例
    let bValue = 38;
    Object.defineProperty(o, "b", {

        // 访问o.b的值时 通过 getter 获取
        get() {
            console.log("访问o.b的值时 通过 getter 获取");
            return bValue;
        },

        // 设置obj.b的值时 通过 setter 进行
        set(newValue) {
            console.log("设置obj.b的值时 通过 setter 进行", newValue);
            bValue = newValue;
        },

        enumerable: true,

        writable: true,
    });

    o.b = 39

    console.log(o.b);//
    // 'b' 属性存在于对象 o 中，其值为 38。
    // o.b 的值现在始终与 bValue 相同，除非重新定义了 o.b。



    // // 数据描述符和访问器描述符不能混合使用
    // Object.defineProperty(o, "conflict", {
    //     value: 0x9f91102,
    //     get() {
    //         return 0xdeadbeef;
    //     },
    // });
    // 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors

}

/* GPT：Object.defineProperty给一个尽量详细的案例 */
function fn3() {
    // 创建一个空对象
    const obj = {};

    // 使用 Object.defineProperty 定义属性
    Object.defineProperty(obj, 'name', {
        value: 'John',             // 属性值为 'John'
        configurable: true,         // 可配置
        enumerable: true,          // 可枚举
        writable: false,           // 不可写
    });

    // 尝试更改属性值（不可写，所以不会生效）
    obj.name = 'Jane';

    // 打印属性值
    console.log(obj.name);      // 输出: "John"

    console.log("==========");
    for (let key in obj) {
        console.log(key, obj[key]);
    }
    console.log("==========");

    // 尝试重新定义属性特性（可配置，所以可以重新定义）
    Object.defineProperty(obj, 'name', {
        enumerable: false,         // 不可枚举
        writable: true            // 可写
    });

    // 打印属性值
    obj.name = 'Jane';
    console.log(obj.name);      // 输出: "John"

    console.log("==========");
    for (let key in obj) {
        console.log(key, obj[key]);
    }
    console.log("==========");




    // 打印obj.name的属性描述符
    const propertyDescriptor = Object.getOwnPropertyDescriptor(obj, 'name');
    console.log(propertyDescriptor);

    // 删除属性（可配置，所以可以删除）
    delete obj.name;

    // 打印对象
    console.log(obj);
}

/* data的结构无法预知 要求data中任何属性（递归到每一个叶子节点）的改变都能侦听到 */
function fn4() {

    /* 好比是一个组件的数据 */
    const data = {
        name: 'John',
        age: 30,
        hobbies: ['reading', 'writing'],
        dog: {
            nickname: "臭皮",
            legs: 4,
        }
    };

    /* 将data中的每一个叶子结点 */
    // let _age = 30
    // Object.defineProperty(
    //     data,
    //     "age",

    //     /* data.age的属性描述符 */
    //     {
    //         /* 不能同时设置 【getter/setter】 和 【value/writable】 */
    //         // value: 30,
    //         // writable: true,

    //         enumerable: true,

    //         get() {
    //             console.log("getter called");
    //             return _age
    //         },

    //         set(newValue) {
    //             console.log("setter called");
    //             _age = newValue
    //         }
    //     }
    // )
    // data.age = 60
    // console.log(data.age);

    // /* 侦听一个更深层次的叶子节点 */
    // let hobby0 = "reading"
    // Object.defineProperty(
    //     data.hobbies,
    //     0,
    //     {
    //         get() {
    //             console.log("getter hobbies[0]");
    //             return hobby0
    //         },

    //         set(newValue) {
    //             console.log("setter hobbies[0]");
    //             hobby0 = newValue
    //         }
    //     }
    // )

    // data.hobbies[0] = "留黄书"
    // console.log(data.hobbies[0]);

    /* 像深拷贝一样递归遍历到每一个叶子节点 */
    function watchObjProps(obj) {
        for (let key in obj) {

            /* 首先将当前节点劫持监听起来 */
            let _value = obj[key]

            Object.defineProperty(
                obj,
                key,
                {
                    get() {
                        // console.log("get", key);
                        return _value
                    },
                    set(newValue) {
                        // console.log("set", key, newValue);
                        _value = newValue
                    }
                }
            )

            if (typeof _value === "object") {
                watchObjProps(_value)
            }
        }

        return obj
    }
    watchObjProps(data)

    /* 访问普通叶子节点 */
    data.name = "JACK"
    data.age = 60
    console.log(data.name, data.age);

    /* 访问深层叶子节点 */
    data.hobbies[1] = "写作"
    // console.log(data.hobbies);
    console.log(data.hobbies[0], data.hobbies[1]);

    data.dog.legs = 3
    console.log(data.dog.legs);

}

function fn5() {

    /* 好比是一个组件的数据 */
    const data = {
        name: 'John',
        age: 30,
        hobbies: ['reading', 'writing'],
        dog: {
            nickname: "臭皮",
            legs: 4,
        }
    };

    /* 像深拷贝一样递归遍历到每一个叶子节点 */
    function watchObjProps(obj) {
        for (let key in obj) {

            /* 首先将当前节点劫持监听起来 */
            let _value = obj[key]
            Object.defineProperty(
                obj,
                key,
                {
                    get() {
                        // console.log("get", key);
                        return _value
                    },
                    set(newValue) {
                        // console.log("set", key, newValue);
                        _value = newValue
                    }
                }
            )

            if (typeof _value === "object") {
                watchObjProps(_value)
            }
        }
    }
    watchObjProps(data)

    /* 访问普通叶子节点 */
    data.name = "JACK"
    data.age = 60
    console.log(data.name, data.age);

    /* 访问深层叶子节点 */
    data.hobbies[1] = "写作"
    // console.log(data.hobbies);
    console.log(data.hobbies[0], data.hobbies[1]);
    data.dog.legs = 3
    console.log(data.dog.legs);

}

/* 选学内容 */
function fn6() {

    /* 好比是一个组件的数据 */
    const data = {
        name: 'John',
        age: 30,
        // hobbies: ['reading', 'writing'],
        // dog: {
        //     nickname: "臭皮",
        //     legs: 4,
        // }
    };

    /* 像深拷贝一样递归遍历到每一个叶子节点 */
    function watchObjProps(obj) {
        console.log("obj1", obj);

        for (let key in obj) {

            /* 首先将当前节点劫持监听起来 */
            let _value = obj[key]
            Object.defineProperty(
                obj,
                key,
                {
                    get() {
                        // console.log("get", key);
                        return _value
                    },
                    set(newValue) {
                        // console.log("set", key, newValue);
                        _value = newValue
                    }
                }
            )

            if (typeof _value === "object") {
                watchObjProps(_value)
            }
        }

        console.log("obj2", obj);
    }
    watchObjProps(data)

    console.log(data.hobbies);

}

~function main() {
    fn6()
}()